package org.budo.support.lang.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.budo.support.assertion.Assert;
import org.budo.support.slf4j.Slf4j;
import org.budo.time.Time;
import org.slf4j.Logger;
import org.springframework.core.DefaultParameterNameDiscoverer;

/**
 * @author limingwei
 * @date 2016年6月16日 下午4:00:06
 */
public class ReflectUtil {
    private static final Logger log = Slf4j.getLogger();

    private static final String _DUBBO_BYTECODE_PROXY = "com.alibaba.dubbo.common.bytecode.proxy";

    private static final Map<String, Method> _METHOD_CACHE = new ConcurrentHashMap<String, Method>();

    private static final Map<String, Field> _Field_CACHE = new ConcurrentHashMap<String, Field>();

    public static Field findFieldCached(Class<?> targetType, String fieldName) {
        String key = targetType.getName() + "." + fieldName;

        Field field = _Field_CACHE.get(key);
        if (null != field) {
            return field;
        }

        field = findField(targetType, fieldName);
        if (null == field) {
            return null;
        }

        _Field_CACHE.put(key, field);
        return field;
    }

    public static Field findField(Class<?> targetType, String fieldName) {
        List<Field> allFields = getAllFields(targetType);

        for (Field field : allFields) {
            String name = field.getName();

            if (name.equalsIgnoreCase(fieldName)) {
                return field;
            }

            if (name.equalsIgnoreCase(fieldName.replace("_", ""))) {
                return field;
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("#76 getField_2, targetType=" + targetType + ", fieldName=" + fieldName, new Throwable());
        }

        return null;
    }

    /**
     * 如果方法不存在，就抛NoSuchMethodException
     */
    public static Method getMethod(Class<?> targetType, String name, Class<?>[] parameterTypes) {
        String methodCacheKey = makeMethodString(targetType, name, parameterTypes);
        if (_METHOD_CACHE.containsKey(methodCacheKey)) {
            return _METHOD_CACHE.get(methodCacheKey);
        }

        try {
            Method method = targetType.getMethod(name, parameterTypes); // getMethod
            _METHOD_CACHE.put(methodCacheKey, method);
            return method;
        } catch (NoSuchMethodException e) {
            try {
                Method method = targetType.getDeclaredMethod(name, parameterTypes); // getDeclaredMethod
                _METHOD_CACHE.put(methodCacheKey, method);
                return method;
            } catch (Throwable e2) {
                throw new RuntimeException("#233 targetType=" + targetType + ", name=" + name + ", e=" + e + ", e2=" + e2, e2);
            }
        }
    }

    /**
     * @return 如果方法不存在，就返回null
     */
    public static Method findMethod(Class<?> targetType, String name, Class<?>[] parameterTypes) {
        String methodCacheKey = makeMethodString(targetType, name, parameterTypes);
        if (_METHOD_CACHE.containsKey(methodCacheKey)) {
            return _METHOD_CACHE.get(methodCacheKey);
        }

        try {
            Method method = targetType.getMethod(name, parameterTypes);
            _METHOD_CACHE.put(methodCacheKey, method);
            return method;
        } catch (NoSuchMethodException e) {
            return null;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static Boolean hasField(Class<?> type, String field) {
        if (null == type || null == field) {
            log.error("#129 hasField, type=" + type + ", field=" + field);
            return false;
        }

        try {
            type.getDeclaredField(field);// 有此属性
            return true;
        } catch (Exception e) {
            Class<?> superType = type.getSuperclass();
            if (superType.equals(Object.class)) {
                return false;
            }

            return hasField(superType, field);
        }
    }

    public static Class<?> getSuperActualTypeArgument(Class<?> type, Integer index) {
        Type[] actualTypeArguments = getSuperActualTypeArguments(type);

        if (null == actualTypeArguments || actualTypeArguments.length < 1) {
            log.error("#141 getSuperActualTypeArgument, type=" + type + ", index=" + index);
            return null;
        }

        Type actualType = actualTypeArguments[index];
        if (actualType instanceof ParameterizedType) { // Set<String> -> Set.class
            return (Class<?>) ((ParameterizedType) actualType).getRawType();
        }

        return (Class<?>) actualType;
    }

    public static Type[] getSuperActualTypeArguments(Class<?> type) {
        Type genericType = type.getGenericSuperclass();

        if (null == genericType || !(genericType instanceof ParameterizedType)) {
            log.error("#154 getActualTypeArguments, type=" + type);
            return null;
        }

        ParameterizedType parameterizedType = (ParameterizedType) genericType;
        return parameterizedType.getActualTypeArguments();
    }

    private static void makeAccessible(Field field) {
        if (field.isAccessible()) {
            return;
        }

        field.setAccessible(true);

        Field modifiersField = getDeclaredField_0(Field.class, "modifiers");
        if (null == modifiersField) {
            return;
        }

        modifiersField.setAccessible(true);

        try {
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        } catch (Throwable e) {
            log.error("#83 field makeAccessible error, field=" + field + ", e=" + e, e);
        }

    }

    public static void setFieldValue(Object object, Field field, Object value) {
        try {
            makeAccessible(field);

            field.set(object, value);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static void setFieldValue(Object object, String fieldName, Object value) {
        Field field = getDeclaredField(object.getClass(), fieldName);
        if (field == null) {
            throw new IllegalArgumentException("#107 ould not find field [" + fieldName + "] on target [" + object + "]");
        }

        setFieldValue(object, field, value);
    }

    /**
     * 获取静态属性(常量)的值
     */
    public static Object getFieldValue(Field field) {
        try {
            makeAccessible(field);
            return field.get(null);
        } catch (Throwable e) {
            throw new RuntimeException("#103 getFieldValue error, e=" + e + ", field=" + field);
        }
    }

    public static Object getFieldValue(Class<?> targetType, String fieldName, Object target) {
        if (null == targetType || null == fieldName || null == target) {
            throw new RuntimeException("#127 getFieldValue error, targetType=" + targetType + ", fieldName=" + fieldName + ", target=" + target);
        }

        Field field = getDeclaredField(targetType, fieldName);
        if (null == field) {
            throw new RuntimeException("#128 getFieldValue error, targetType=" + targetType + ", fieldName=" + fieldName);
        }

        try {
            makeAccessible(field);
            return field.get(target);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static Object getFieldValue(Class<?> targetType, String fieldName) {
        try {
            Field field = getDeclaredField(targetType, fieldName);
            makeAccessible(field);
            return field.get(null);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static Object getFieldValue(Object target, String fieldName) {
        Class<?> type = target.getClass();

        Field field = getDeclaredField_0(type, fieldName);
        if (null == field) {
            throw new IllegalArgumentException("#112 Could not find field [" + fieldName + "] on target [" + target + "]");
        }

        try {
            makeAccessible(field);
            return field.get(target);
        } catch (Throwable e) {
            throw new RuntimeException("#119 getFieldValue error, e=" + e + ", field=" + field);
        }
    }

    public static Object getFieldValue_0(Object object, String fieldName) {
        Class<?> type = object.getClass();

        Field field = getDeclaredField_0(type, fieldName);
        if (null == field) {
            log.error("#162 Could not find field [" + fieldName + "] on target [" + object + "]");
            return null;
        }

        try {
            makeAccessible(field);
            return field.get(object);
        } catch (Throwable e) {
            throw new RuntimeException("#119 getFieldValue error, e=" + e + ", field=" + field);
        }
    }

    /**
     * @see #getDeclaredField(Class, String)
     */
    public static Field getDeclaredField_0(Class<?> targetType, String fieldName) {
        try {
            return targetType.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            return null;
        }
    }

    /**
     * @see #getDeclaredField_0(Class, String)
     */
    public static Field getDeclaredField(Class<?> targetType, String fieldName) {
        try {
            return targetType.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            if (targetType.equals(Object.class)) {
                throw new RuntimeException("#75 NoSuchFieldException, targetType=" + targetType + ", fieldName=" + fieldName, e);
            }

            return getDeclaredField(targetType.getSuperclass(), fieldName);
        }
    }

    public static Field getField(Class<?> targetType, String fieldName) {
        return getDeclaredField(targetType, fieldName);
    }

    public static List<Method> getAllMethods(Class<?> type) {
        if (null == type) {
            return null;
        }

        List<Method> methods = new ArrayList<Method>();
        Method[] declaredMethods = type.getDeclaredMethods();
        List<Method> declaredMethodList = ListUtil.toList(declaredMethods);
        methods.addAll(declaredMethodList);

        if (!Object.class.equals(type)) {
            Class<?> superType = type.getSuperclass();
            List<Method> superMethods = getAllMethods(superType);
            ListUtil.addAll(methods, superMethods);
        }
        return methods;
    }

    @SuppressWarnings("rawtypes")
    public static Class classForName(String type) {
        if (null == type || type.isEmpty()) {
            log.error("#224 classForName, return null, type=" + type);
            return null;
        }

        try {
            return Class.forName(type);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public static List<Class<?>> getAllInterfaces(Class<?> beanType) {
        List<Class<?>> list = new ArrayList<Class<?>>();
        Class<?>[] interfaceTypes = beanType.getInterfaces();
        for (Class<?> type : interfaceTypes) {
            list.add(type);
        }

        Class<?> superType = beanType.getSuperclass();
        if (!Object.class.equals(superType)) {
            List<Class<?>> superInterfaces = getAllInterfaces(superType);
            list.addAll(superInterfaces);
        }
        return list;
    }

    public static Boolean isInterface(String typeName) {
        Class type = classForName(typeName);
        return type.isInterface();
    }

    public static Boolean hasClass(String typeName) {
        try {
            Class.forName(typeName);
            return true;
        } catch (Throwable e) {
            return false;
        }
    }

    public static List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new ArrayList<Field>();
        Field[] declaredFields = type.getDeclaredFields();
        Collections.addAll(fields, declaredFields);

        Class<?> superclass = type.getSuperclass();
        if (null != superclass && Object.class != superclass) {// 扫描超类的Field
            List<Field> superFields = getAllFields(superclass);
            fields.addAll(superFields);
        }

        return fields;
    }

    public static Object newInstance(Class<?> type) {
        try {
            return type.newInstance();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static <T extends Annotation> T getAnnotation(Class<?> targetType, Class<T> annotationType) {
        if (null == targetType || null == annotationType) {
            return null;
        }

        T annotation = targetType.getAnnotation(annotationType);
        if (null != annotation) {
            return annotation;
        }

        if (!targetType.equals(Object.class)) {
            return getAnnotation(targetType.getSuperclass(), annotationType);
        }

        return null;
    }

    public static void setFieldValue(Class<?> targetType, String fieldName, Object target) {
        try {
            Field field = getDeclaredField(targetType, fieldName);
            makeAccessible(field);
            field.set(null, target);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 返回方法形参名列表，有可能会取不到
     */
    public static String[] getMethodParameterNames(Method method) {
        if (null == method) {
            return null;
        }

        String[] parameterNames = _getMethodParameterNames(method); // 递归调用
        if (null != parameterNames) {
            return parameterNames;
        }

        Class<?> declaringType = method.getDeclaringClass();
        if (declaringType.equals(Object.class)) { // Object 的方法就不打日志
            return null;
        }

        String declaringTypeName = declaringType.getName();
        if (declaringTypeName.startsWith(_DUBBO_BYTECODE_PROXY)) {
            return null;
        }

        // 打日志并返回空
        log.error("#203 getMethodParameterNames error, method=" + method + ", parameterNames=" + parameterNames);
        return null;
    }

    /**
     * 私有方法,递归调用
     */
    private static String[] _getMethodParameterNames(Method method) {
        DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);

        if (null != parameterNames) {
            return parameterNames;
        }

        Class<?> superType = method.getDeclaringClass().getSuperclass();
        if (null != superType && !superType.equals(Object.class)) {
            Method superMethod = ReflectUtil.getMethod(superType, method.getName(), method.getParameterTypes());
            String[] superMethodParameterNames = _getMethodParameterNames(superMethod); // 递归调用
            return superMethodParameterNames;
        }

        return null;
    }

    public static Object invokeMethod(Object target, Method method, Object[] args) {
        if (null == method) {
            log.error("#477 invokeMethod, target=" + target + ", method=" + method + ", args=" + Arrays.toString(args));
            return null;
        }

        try {
            method.setAccessible(true);
            return method.invoke(target, args);
        } catch (Throwable e) {
            throw new RuntimeException("#411 invokeMethod error, target=" + target + ", method=" + method + ", args=" + Arrays.toString(args), e);
        }
    }

    /**
     * 调用标准的get方法
     */
    public static <T> T invokeGet(Object obj, String fieldName) {
        Assert.notNull(obj, "obj must not be null");
        Assert.notNull(fieldName, "fieldName must not be null");

        String methodName = StringUtil.buildGet(fieldName);
        Method method = getMethod(obj.getClass(), methodName, null);
        return (T) invokeMethod(obj, method, null);
    }

    /**
     * 调用标准的set方法
     */
    public static <T> void invokeSet(T obj, Field field, Object param) {
        Assert.notNull(obj, "obj must not be null");
        Assert.notNull(field, "field must not be null");

        Class<?>[] classes = new Class<?>[] { field.getType() };

        String methodName = StringUtil.buildSet(field.getName());
        Method method = findMethod(obj.getClass(), methodName, classes);

        invokeMethod(obj, method, new Object[] { param });
    }

    public static boolean hasMethod(Class<?> targetType, String name, Class[] parameterTypes) {
        return null != findMethod(targetType, name, parameterTypes);
    }

    public static Boolean isPrimitiveTypes(Class<?> type) {
        if (null == type) {
            return false;
        }

        return type.isPrimitive() //
                || String.class.equals(type) //
                || Boolean.class.equals(type) //
                || Character.class.equals(type) //
                || Time.isTimeType(type) // 时间
                || isNumberType(type); // 数字
    }

    public static Boolean isBasicType(Class<?> type) {
        if (null == type) {
            return false;
        }

        return isPrimitiveTypes(type) || Date.class.isAssignableFrom(type);
    }

    /**
     * @see Method#toGenericString()
     */
    public static String methodToString(Method method) {
        if (null == method) {
            return "<method-is-null>";
        }

        try {
            StringBuilder stringBuilder = new StringBuilder();

            stringBuilder.append(method.getDeclaringClass().getName()).append('.');
            stringBuilder.append(method.getName()).append('(');
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int j = 0; j < parameterTypes.length; j++) {
                Class<?> parameterType = parameterTypes[j];

                String parameterTypeString;
                if (parameterType.isArray()) {
                    parameterTypeString = parameterType.getComponentType().getCanonicalName() + "...";
                } else {
                    parameterTypeString = parameterType.getCanonicalName();
                }
                stringBuilder.append(parameterTypeString);

                if (j < (parameterTypes.length - 1)) {
                    stringBuilder.append(',');
                }
            }
            stringBuilder.append(')');
            return stringBuilder.toString();
        } catch (Throwable e) {
            log.error("#357 methodToString error, e=" + e, e);
            return "<" + e + ">";
        }
    }

    public static String makeMethodString(Class<?> targetType, String name, Class<?>[] parameterTypes) {
        Assert.notNull(targetType, "#2234 targetType must not be null");
        Assert.notNull(name, "#2235 name must not be null");

        StringBuilder result = new StringBuilder();
        result.append(targetType.getCanonicalName()).append(".").append(name).append("(");
        if (parameterTypes != null && parameterTypes.length > 0) {
            for (int i = 0; i < parameterTypes.length; i++) {
                result.append(parameterTypes[i].getCanonicalName());
                if (i != parameterTypes.length - 1) {
                    result.append(",");
                }
            }
        }
        result.append(")");

        return result.toString();
    }

    /**
     * 传入一个（类的）方法，返回此类实现的接口上的对应方法
     */
    public static List<Method> findInterfaceMethods(Method method) {
        if (null == method) {
            return null;
        }

        Class<?> declaringType = method.getDeclaringClass();
        Class<?>[] interfaceTypes = declaringType.getInterfaces(); // 似乎返回的是直接接口，间接的未返回

        List<Method> interfaceMethods = new ArrayList<Method>();
        if (null != interfaceTypes) {
            for (Class<?> interfaceType : interfaceTypes) {
                Method interfaceMethod = ReflectUtil.findMethod(interfaceType, method.getName(), method.getParameterTypes());
                if (null != interfaceMethod) {
                    interfaceMethods.add(interfaceMethod);
                }
            }
        }

        return interfaceMethods;
    }

    /**
     * 传入一个（类的）方法，返回此类继承的超类上的对应方法
     */
    public static List<Method> findSuperMethods(Method method) {
        if (null == method) {
            return null;
        }

        Class<?> declaringType = method.getDeclaringClass();
        Class<?> superType = declaringType.getSuperclass(); // 似乎返回的是直接接口，间接的未返回

        List<Method> superMethods = new ArrayList<Method>();
        if (null != superType) {
            Method interfaceMethod = ReflectUtil.findMethod(superType, method.getName(), method.getParameterTypes());
            if (null != interfaceMethod) {
                superMethods.add(interfaceMethod);
            }
        }

        return superMethods;
    }

    public static boolean hasAnnotationPresent(Class clazz, Class<? extends Annotation> annoClass) {
        Assert.notNull(clazz, "clazz must not be null");
        Assert.notNull(annoClass, "annoClass must not be null");

        if (!clazz.isAnnotationPresent(annoClass)) {
            return false;
        }
        return true;
    }

    public static boolean hasAnnotationPresent(Field field, Class<? extends Annotation> annoClass) {
        Assert.notNull(field, "field must not be null");
        Assert.notNull(annoClass, "annoClass must not be null");
        if (!field.isAnnotationPresent(annoClass)) {
            return false;
        }
        return true;
    }

    /**
     * 查询类的所有属性的集合
     */
    public static Set<String> getAllAttribute(Class<?> clazz) {
        Assert.notNull(clazz, "clazz must not be null");

        Set<String> attributes = new HashSet<String>();

        List<Field> allFields = getAllFields(clazz);
        if (!CollectionUtil.isEmpty(allFields)) {
            for (Field field : allFields) {
                attributes.add(field.getName());
            }
        }

        return attributes;
    }

    /**
     * 判断某个类中是否存在某个属性被某个注解注释
     */
    public static boolean hasFieldAnnotationBy(Class<?> clazz, Class<? extends Annotation> annotation) {
        Assert.notNull(clazz, "clazz must not be null");
        Assert.notNull(annotation, "annotation must not be null");

        List<Field> allFields = getAllFields(clazz);
        if (CollectionUtil.isEmpty(allFields)) {
            return false;
        }

        for (Field field : allFields) {
            if (hasAnnotationPresent(field, annotation)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 是否是静态属性
     */
    public static boolean isStatic(Field field) {
        Assert.notNull(field, "field must not be null");
        return Modifier.isStatic(field.getModifiers());
    }

    /**
     * 是否是final属性
     */
    public static boolean isFinal(Field field) {
        Assert.notNull(field, "field must not be null");
        return Modifier.isFinal(field.getModifiers());
    }

    /**
     * 返回指定类型上的所有常量
     */
    public static Map<String, Object> constantValues(Class<?> type) {
        if (null == type) {
            return null;
        }

        Map<String, Object> map = new HashMap<String, Object>();

        List<Field> fields = getAllFields(type);
        for (Field field : fields) {
            String name = field.getName();
            Object value = getFieldValue(field);

            map.put(name, value);
        }

        return map;
    }

    /**
     * 有无参构造函数
     */
    public static boolean hasNoArgConstructor(Class<?> type) {
        Constructor<?>[] constructors = type.getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (0 == constructor.getParameterTypes().length) {
                return true;
            }
        }

        return false;
    }

    public static Class<?>[] _classForNameArray(String[] types) {
        if (null == types) {
            return null;
        }

        Class<?>[] array = new Class<?>[types.length];
        for (int i = 0; i < types.length; i++) {
            array[i] = classForName(types[i]);
        }

        return array;
    }

    public static Object newInstance(String type) {
        Class clazz = classForName(type);
        return newInstance(clazz);
    }

    /**
     * @see #invokeMethod(Class, String, Class[], Object, Object[])
     */
    public static Object invokeMethod_2(String targetType, String methodName, Class[] parameterTypes, Object target, Object[] args) {
        Class type = classForName(targetType);
        return invokeMethod(type, methodName, parameterTypes, target, args);
    }

    public static Object invokeMethod(Class<?> targetType, String methodName, Class[] parameterTypes, Object target, Object[] args) {
        Method method = getMethod(targetType, methodName, parameterTypes);
        return invokeMethod(target, method, args);
    }

    public static Constructor<?> getConstructor(Class<?> type, Class<?>[] parameterTypes) {
        try {
            return type.getConstructor(parameterTypes);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static Object newInstance(Constructor<?> constructor, Object[] args) {
        try {
            return constructor.newInstance(args);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public static Class<?> classForNameOrNull(String className) {
        if (StringUtil.isNullOrEmpty(className)) {
            log.error("#691 classForNameOrNull, className=" + className);
            return null;
        }

        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.info("#805 classForNameOrNull, className=" + className);
            return null;
        } catch (Throwable e) {
            log.error("#695 classForNameOrNull return null, className=" + className + ", e=" + e);
            return null;
        }
    }

    public static Boolean isCollection(Object val) {
        return null != val && val instanceof Collection;
    }

    public static Boolean isArray(Object val) {
        return null != val && val.getClass().isArray();
    }

    public static Method[] getMethods_2(Class<?> type) {
        Method[] methods = type.getMethods();

        List<Method> methodList = new ArrayList<Method>();

        for (Method method : methods) {
            Class<?> declaringClass = method.getDeclaringClass();
            if (Object.class.equals(declaringClass)) {
                continue;
            }

            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }

            methodList.add(method);
        }

        // 排序
        Collections.sort(methodList, new Comparator<Method>() {
            @Override
            public int compare(Method o1, Method o2) {
                int compareTo = o1.getName().compareTo(o2.getName());
                if (0 != compareTo) {
                    return compareTo; // 方法名不同的
                }

                // 方法名相同的
                return o1.toGenericString().compareTo(o2.toGenericString());
            }
        });

        return methodList.toArray(new Method[0]);
    }

    public static String methodNameAndArgs(String method) {
        if (null == method || method.trim().isEmpty()) {
            log.error("#782 methodNameAndArgs return null, method = " + method);
            return null;
        }

        int a = method.lastIndexOf('(');
        int b = method.substring(0, a).lastIndexOf('.');

        return method.substring(b + 1);
    }

    public static String methodDeclaringClassName(String method) {
        if (null == method || method.trim().isEmpty()) {
            log.error("#794 methodNameAndArgs return null, method = " + method);
            return null;
        }

        int a = method.lastIndexOf('(');
        int b = method.substring(0, a).lastIndexOf('.');

        return method.substring(0, b);
    }

    public static Boolean isAssignableFrom(String superType, String thisType) {
        Class<?> _superType = classForName(superType);
        Class<?> _thisType = classForName(thisType);
        return _superType.isAssignableFrom(_thisType);
    }

    public static Boolean isAssignableFrom(Class<?> superType, Class<?> thisType) {
        if (null == superType || null == thisType) {
            log.error("#821 isAssignableFrom, superType=" + superType + ", thisType=" + thisType);
            return false;
        }

        return superType.isAssignableFrom(thisType);
    }

    public static String[] classNameArray(Class<?>[] types) {
        if (null == types) {
            return null;
        }

        if (types.length < 1) {
            return new String[0];
        }

        String[] names = new String[types.length];
        for (int i = 0; i < types.length; i++) {
            names[i] = types[i].getName();
        }

        return names;
    }

    public static Object invokeMethod_3(String targetType, String methodName, String[] parameterTypes, Object target, Object[] args) {
        Class<?> _targetType = classForName(targetType);
        Class<?>[] _parameterTypes = _classForNameArray(parameterTypes);
        Method method = getMethod(_targetType, methodName, _parameterTypes);

        try {
            return invokeMethod(target, method, args);
        } catch (Throwable e) {
            throw new RuntimeException("#863 invokeMethod_3 error, targetType=" + targetType + ", methodName=" + methodName + ", parameterTypes=" + Arrays.toString(parameterTypes) //
                    + ", target=" + target + ", args=" + Arrays.toString(args) + ", e=" + e, e);
        }
    }

    public static String simpleName(String type) {
        String trim = type.trim();
        int a = trim.lastIndexOf('.');
        if (a < 0) {
            log.error("#861 simpleName, a=" + a + ", type=" + type);
            return type;
        }

        String simpleName = type.substring(a + 1);
        return simpleName;
    }

    public static Object toType(Object value, Class<?> parameterType, Type genericType) {
        if (Collection.class.isAssignableFrom(parameterType) //
                && null != genericType //
                && genericType instanceof ParameterizedType //
                && value instanceof Collection) { // 泛型 集合
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type type = parameterizedType.getActualTypeArguments()[0];
            Collection collection = (Collection) value;
            return BeanUtil.listToEntity(collection, (Class) type);
        }

        // 其他 包括 单个对象
        return toType(value, parameterType);
    }

    public static <T> T toType(Object input, Class<T> type) {
        if (null == input || null == type) {
            return (T) input;
        }

        if (String.class.equals(type)) {
            return (T) input.toString();
        }

        if (boolean.class.equals(type) || Boolean.class.equals(type)) {
            String in = input.toString().trim();
            Object out = "true".equalsIgnoreCase(in) || "1".equalsIgnoreCase(in);
            return (T) out;
        }

        // boolean 也 被判断为 isNumberType 了 ？
        // 所以 boolean 要放在 Number 前面
        if (ReflectUtil.isNumberType(type)) {
            return (T) NumberUtil.toNumber(input, type);
        }

        if (ReflectUtil.isTimeType(type)) {
            return Time.when(input).toType(type);
        }

        if (input instanceof Map) {
            return (T) BeanUtil.mapToEntity((Map) input, type);
        }

        if (input instanceof List) {
            return (T) BeanUtil.listToEntity((List) input, type);
        }

        if (type.isEnum()) {
            return (T) toEnum(input, type);
        }

        log.error("#931 input=" + input + ", type=" + type);
        return null;
    }

    public static Enum toEnum(Object input, Class<?> type) {
        Object[] enums = type.getEnumConstants();
        for (Object each : enums) {
            Enum _enum = (Enum) each;
            String name = _enum.name();
            if (("" + input).equals(name)) {
                return _enum;
            }
        }

        return null;
    }

    public static Boolean isNumberType(Class<?> type) {
        if (null == type) {
            return false;
        }

        return type.isPrimitive() || Number.class.isAssignableFrom(type);
    }

    private static Boolean isTimeType(Class<?> type) {
        if (null == type) {
            return false;
        }

        return Date.class.isAssignableFrom(type);
    }

    public static Class<?> beanType(Class<?> type) {
        if (null == type) {
            log.error("#973 beanType, type=" + type);
            return null;
        }

        while (type.getName().contains("$$EnhancerBySpringCGLIB$$")) {
            Class<?> superType = type.getSuperclass();
            return beanType(superType);
        }

        return type;
    }

    public static Map newMap(Class<?> type) {
        if (type.isInterface()) {
            return new HashMap();
        }

        return (Map) newInstance(type);
    }

    public static Class<?> getActualTypeArgument(Class<?> type, Integer index) {
        return getSuperActualTypeArgument(type, index);
    }

    /**
     * 合并一个JavaBean, 如果obj1中的值为空，使用obj2的值来填充。
     */
    public static <T extends Object> T combinationBean(Object obj1, Object obj2) {
        Assert.notNull(obj1, "The parameter[obj1] is null.");
        Assert.notNull(obj2, "The parameter[obj2] is null.");

        Class<?> obj1Class = obj1.getClass();
        Class<?> obj2Class = obj2.getClass();

        if (!StringUtil.equals(obj1Class.getName(), obj2Class.getName())) {
            throw new IllegalArgumentException("#0913 The obj1[" + obj1Class.getName() + "] is inconsistent with the type of obj2[" + obj2Class.getName() + "]!");
        }

        List<Field> obj1Fields = ReflectUtil.getAllFields(obj1Class);
        List<Field> obj2Fields = ReflectUtil.getAllFields(obj2Class);
        try {
            for (int i = 0; i < obj1Fields.size(); i++) {
                Field obj1Field = obj1Fields.get(i), obj2Field = obj2Fields.get(i);
                if (Modifier.isStatic(obj1Field.getModifiers())) {
                    continue;
                }
                obj1Field.setAccessible(true);
                obj2Field.setAccessible(true);

                if (StringUtil.equalsIgnoreCase("serialVersionUID", obj1Field.getName())) {
                    continue;
                }

                // 如果obj1Field为空，使用obj2Field中的值填充。
                if (null == obj1Field.get(obj1)) {
                    obj1Field.set(obj1, obj2Field.get(obj2));
                }
            }
        } catch (Exception e) {
            log.error("#1083 ", e);
        }
        return (T) obj1;
    }
}
